#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <time.h>
#include <fcntl.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>		/* close */
#include <netdb.h> 


/* 手动封装一个http的请求 */
#define HTTP_VERSION        "HTTP/1.1"
#define USER_AGENT "Mozilla/5.0 (Windows NT 5.1; rv:10.0.2) Gecko/20100101 Firefox/10.0.2"
#define CONNECTION "close"

#define BUFFER_SIZE		4096

char* host_to_ip(char* hostname)
{
    struct hostent* host_entry = gethostbyname(hostname);
    if(host_entry) {
        return inet_ntoa(*(struct in_addr*)*host_entry->h_addr_list);
    } else {
        return NULL;
    } 
}

int http_creat_socket(char *ip)
{
    int socket_fd = 0;

    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(socket_fd < 0)   {
        printf("socket fd fail\n");
        return 0;
    }

    struct sockaddr_in servaddr = {0};

    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(80);
    servaddr.sin_addr.s_addr = inet_addr(ip);
    if( connect(socket_fd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in)) < 0) {
        return -1;
    }

    return socket_fd;
}


int http_send_request(int socket_fd, char* hostname, char* resource) {

    char buffer[BUFFER_SIZE] = {0};

    int len = sprintf(buffer, 
"GET %s %s\r\n\
Host: %s\r\n\
Connection: %s\r\n\
\r\n", resource, HTTP_VERSION, hostname, CONNECTION //最后需要一个回车换行符
    );

    send(socket_fd, buffer, len, 0);

    ssize_t ret = recv(socket_fd, buffer, 4096, 0);
    printf("bufferm %s\n", buffer);
    return ret;
}

int http_commit(char *hostname, char *resouce)
{
   // char *hostname = argv[1];
    //char *resouce = argv[2];

    char *ip = host_to_ip(hostname);
    if(ip == NULL) {
        printf("get ip fail\n");
        return -1;
    }

    int sockfd = http_creat_socket(ip);
    if(sockfd < 0 ) {
        printf("http_creat_socket fiale\n");
        return -2;
    }

    http_send_request(sockfd, hostname, resouce);
    
    close(sockfd);
    return 0;
}

//https://api.seniverse.com/v3/weather/now.json?key=SgYc2XaulwRKnpSRF&location=beijing&language=zh-Hans&unit=c

char *quest[] = {
    "api.seniverse.com", "/v3/weather/now.json?key=SgYc2XaulwRKnpSRF&location=beijing&language=zh-Hans&unit=c",
    "api.seniverse.com", "/v3/weather/now.json?key=SgYc2XaulwRKnpSRF&location=shenzhen&language=zh-Hans&unit=c",
    "api.seniverse.com", "/v3/weather/now.json?key=SgYc2XaulwRKnpSRF&location=shanghai&language=zh-Hans&unit=c",
    "api.seniverse.com", "/v3/weather/now.json?key=SgYc2XaulwRKnpSRF&location=guangzhou&language=zh-Hans&unit=c",
    "api.seniverse.com", "/v3/weather/now.json?key=SgYc2XaulwRKnpSRF&location=changsha&language=zh-Hans&unit=c",
    "api.seniverse.com", "/v3/weather/now.json?key=SgYc2XaulwRKnpSRF&location=leizhou&language=zh-Hans&unit=c",
    "api.seniverse.com", "/v3/weather/now.json?key=SgYc2XaulwRKnpSRF&location=zhanjiang&language=zh-Hans&unit=c"
    };


#include <pthread.h>
#include <sys/epoll.h>

#define ASYNC_CLIENT_NUM		1024
#define HOSTNAME_LENGTH			128

typedef void (*async_result_cb)(const char *hostname, const char *result);

struct async_context {
    int epfd;
    pthread_t thread_id; 
};

struct ep_arg{
    int sockfd;
    char hostname[HOSTNAME_LENGTH];
    async_result_cb cb;
};

void http_async_callback(const char *hostname, const char *result) 
{
    printf("http_async_callback------------------\n");
}

void *epoll_loop(void *arg) 
{
    struct async_context *ctx = (struct async_context *)arg;
    int epfd = ctx->epfd;

    while(1) {
        struct epoll_event events[ASYNC_CLIENT_NUM] = {0};

		int nready = epoll_wait(epfd, events, ASYNC_CLIENT_NUM, -1);
		if (nready < 0) {
			if (errno == EINTR || errno == EAGAIN) {
				continue;
			} else {
				break;
			}
		} else if (nready == 0) {
			continue;
		}

		printf("nready:%d\n", nready);
        int i=0;
        for(i=0; i<nready; i++)  {
            struct ep_arg *ep = (struct ep_arg *)events[i].data.ptr;
            int sockfd = ep->sockfd;

            char buffer[BUFFER_SIZE] = {0};
            ssize_t ret = recv(sockfd, buffer, 4096, 0);

            ep->cb(ep->hostname, buffer);
            printf("bufferm %s\n", buffer);

            epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);

            close(sockfd);
            free(ep);
        }
    }

    return NULL;
}

struct async_context* http_async_client_init() 
{
    int epfd = epoll_create(1);     //参数没多少意义
    if(epfd < 0)  return NULL;

    struct async_context * ctx = malloc(sizeof(struct async_context));
    if(ctx == NULL)  return NULL;

    ctx->epfd = epfd;

    int ret = pthread_create(&ctx->thread_id, NULL, epoll_loop, ctx);
    if(ret < 0) return NULL;
    return ctx;
}

void http_async_client_uint(struct async_context* ctx)
{
    pthread_cancel(ctx->thread_id);
    close(ctx->epfd);
}

int http_async_client_commit(struct async_context* ctx, char *hostname, char *resouce, async_result_cb cb)
{
    char *ip = host_to_ip(hostname);
    if(ip == NULL) {
        printf("get ip fail\n");
        return -1;
    }

    int sockfd = http_creat_socket(ip);
    if(sockfd < 0 ) {
        printf("http_creat_socket fiale\n");
        return -2;
    }

    fcntl(sockfd, F_SETFL, O_NONBLOCK);

    http_send_request(sockfd, hostname, resouce);
    
    struct ep_arg* ep = calloc(1, sizeof(struct ep_arg));
    if(ep == NULL)  return -3;
    ep->sockfd = sockfd;
    ep->cb = cb;
    strcpy(ep->hostname, hostname);

    struct epoll_event ev;
    ev.data.ptr = ep;
    ev.events = EPOLLIN;

    epoll_ctl(ctx->epfd, EPOLL_CTL_ADD, sockfd, &ev);


    //close(sockfd);
    return 0;
}


//king 老师异步四元组

int main(int argc, char *argv[])
{
    #if 0
    //同步的方式
    int i = 0;
    for(i=0; i<sizeof(quest)/sizeof(quest[0]); i+=2) {
        printf("i = %d\n", i);
        http_commit(quest[i], quest[i+1]);
        //sleep(1);
    }
    #else

    struct async_context* ctx = http_async_client_init();

    int i = 0;
    for(i=0; i<sizeof(quest)/sizeof(quest[0]); i+=2) {
        printf("i = %d\n", i);
        http_async_client_commit(ctx, quest[i], quest[i+1], http_async_callback);
        //sleep(1);
    }

    getchar();
    #endif

    return 0;
}